# -*- coding:utf-8 -*-

'''
PROJECT_NAME : auth_demo
file    : app5_oauth
author  : 1032162439@qq.com
date    : 2022-09-09 11:37
IDE     : PyCharm
'''
import json

import jwt
import requests
from jwt import exceptions
from flask import Flask, render_template, request, redirect

# 实例化app对象
app = Flask(__name__)

# 设置secret key
app.secret_key = 'secret key string!'

# github 的 client id、 client secret、 redirect uri
github_client_id = '710286c573a2d51dcf09'
github_client_secret = 'a6fc93ce1ce5a7e8ecb83f843724732ffc25f039'
github_redirect_uri = 'http://127.0.0.1:5000/oauth2/github/callback'
# github 用户信息 api
github_user_info_url = 'https://api.github.com/user'
# github access_token api
github_access_token_url = 'https://github.com/login/oauth/access_token'

# gitee 的 client id、 client secret、 redirect uri
gitee_client_id = '0dd3dc5d005a384d1e5dba633eabfdb59c6bc9d68df8eca374e3245e92156e40'
gitee_client_secret = '2a4d2493c62ff442c6b849a912db73c9bad041d6c485ac6c2a75760cc356e968'
gitee_redirect_uri = 'http://127.0.0.1:5000/oauth2/gitee/callback'
# gitee 用户信息api
gitee_user_info_url = 'https://gitee.com/api/v5/user'
# gitee access_token api
gitee_access_token_url = 'https://gitee.com/oauth/token'


def get_github_user_info(access_token):
    """
    获取github用户信息
    注意：github用户信息api，要求必须以请求头的形式传递token，不能以url方式传参
    :param access_token:
    :return:
    """
    headers = {
        'Authorization': f'token {access_token}'
    }
    resp = requests.get(url=github_user_info_url, headers=headers)
    if resp.status_code == 200:
        return json.loads(resp.text)
    else:
        return {'msg': '认证失败'}


def get_gitee_user_info(access_token):
    """
    获取gitee用户信息
    :param access_token:
    :return:
    """
    resp = requests.get(url=gitee_user_info_url,
                        data={
                            'access_token': access_token
                        })
    if resp.status_code == 200:
        return json.loads(resp.text)
    else:
        return {'msg': '认证失败'}


def get_gitee_access_token(code):
    """
    获取gitee的access token  该token用于向gitee请求用户信息
    :param code:
    :return:
    """
    status = {'status': None, 'msg': None, 'data': ''}
    try:
        resp = requests.post(
            url=gitee_access_token_url,
            data={
                'grant_type': 'authorization_code',
                'client_id': gitee_client_id,
                'client_secret': gitee_client_secret,
                'code': code,
                'redirect_uri': gitee_redirect_uri
            }
        )
        if resp.status_code == 200:
            status['status'] = True
            status['msg'] = 'ok'
            status['data'] = json.loads(resp.text).get('access_token')
    except Exception as e:
        status['status'] = False
        status['msg'] = f'error:{e}'
    return status


def get_github_access_token(code):
    """
    获取github 的access token 用于向github获取用户信息
    :param code:
    :return:
    """
    status = {'status': None, 'msg': None, 'data': ''}
    try:
        resp = requests.post(
            url=github_access_token_url,
            data={
                'client_id': github_client_id,
                'client_secret': github_client_secret,
                'code': code,
            }
        )
        if resp.status_code == 200:
            status['status'] = True
            status['msg'] = 'ok'
            status['data'] = resp.content.decode('utf-8').split('&')[0].split('=')[1]
    except Exception as e:
        status['status'] = False
        status['msg'] = f'error:{e}'
    return status


@app.route('/login')
def login():
    """
    登录接口
    :return:
    """
    context = {
        'github_auth_url': f'https://github.com/login/oauth/authorize?client_id={github_client_id}&redirect_uri={github_redirect_uri}',
        'gitee_auth_url': f'https://gitee.com/oauth/authorize?client_id={gitee_client_id}&redirect_uri={gitee_redirect_uri}&response_type=code'
    }
    return render_template('login.html', context=context)


@app.route('/oauth2/github/callback')
def oauth_github_callback():
    code = request.args.get('code')
    print(f'github:{code}')
    status = get_github_access_token(code)
    print(status)
    access_token = status.get('data')
    github_user_info = get_github_user_info(access_token)
    print(github_user_info)
    jwt_payload = {
        'user_id': github_user_info['id'],
        'user_name': github_user_info['login'],
    }
    jwt = create_jwt(jwt_payload)
    return render_template('index.html', user_info=github_user_info, jwt=jwt)


@app.route('/oauth2/gitee/callback')
def oauth_gitee_callback():
    code = request.args.get('code')
    print(f'gitee: {code}')
    status = get_gitee_access_token(code)
    access_token = status.get('data')
    gitee_user_info = get_gitee_user_info(access_token)
    print(gitee_user_info)
    jwt_payload = {
        'user_id': gitee_user_info['id'],
        'user_name': gitee_user_info['login'],
    }
    jwt = create_jwt(jwt_payload)
    return render_template('index.html', user_info=gitee_user_info, jwt=jwt)


def create_jwt(payload):
    """
    生成token
    :param payload: 需要jwt携带的参数
    :return:
    """
    # 构造header
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    token = jwt.encode(payload=payload, key=app.secret_key, algorithm="HS256", headers=headers)
    return token


def check_jwt(token):
    status = {'status': None, 'msg': None}
    try:
        verified_payload = jwt.decode(jwt=token, key=app.secret_key, algorithms=['HS256'])
        status['status'] = True
        status['msg'] = verified_payload
    except exceptions.ExpiredSignatureError:
        status['msg'] = 'token已失效'
    except jwt.DecodeError:
        status['msg'] = 'token认证失败'
    except jwt.InvalidTokenError:
        status['msg'] = '非法的token'
    return status


# 不需要验证token的白名单
WHITE_LIST = [
    '/login',
    '/oauth2/gitee/callback',
    '/oauth2/github/callback',
]


@app.route('/source')
def source():
    """
    资源接口
    :return:
    """
    return '资源列表'


@app.before_request
def check_jwt_hook():
    """
    拦截请求，校验token是否合法
    :return:
    """
    # 如果请求路径在白名单内
    if request.path in WHITE_LIST:
        return
    # 获取请求路径中的token
    jwt = request.args.get('token')
    if not jwt:
        # 如果没有传递token，重定向到登录页面
        return redirect('/login')
    # 检验jwt
    status = check_jwt(jwt)
    if not status['status']:
        print(status['msg'])
        # 如果jwt校验失败还是重定向到登录页面
        return redirect('/login')
    # jwt校验成功直接到视图函数
    return


if __name__ == '__main__':
    app.run(debug=True)
